21.4 基本插件示例

20 分钟阅读

21.4.1 简单的 Hello World 插件#

插件实现#

// src/plugin.ts import { Plugin, PluginConfig, PluginContext } from '@claude-code/plugin-sdk';

export class HelloWorldPlugin extends Plugin { constructor() { super({ name: 'hello-world', version: '1.0.0', description: 'A simple Hello World plugin' }); }

async initialize(config: PluginConfig): Promise<void> { console.log('Hello World Plugin initialized'); }

async start(): Promise<void> { console.log('Hello World Plugin started'); }

async stop(): Promise<void> { console.log('Hello World Plugin stopped'); }

async cleanup(): Promise<void> { console.log('Hello World Plugin cleaned up'); } }

插件清单#

bash
yaml

# plugin.yaml
name: hello-world
version: 1.0.0
description: A simple Hello World plugin
author: Your Name
license: MIT
main: dist/plugin.js
types: dist/plugin.d.ts

### 使用示例

// 使用插件
import { HelloWorldPlugin } from './plugin';
const plugin = new HelloWorldPlugin();
// 初始化插件
await plugin.initialize({});
// 启动插件
await plugin.start();
// 获取插件状态
const status = plugin.getStatus();
console.log(status);
// 停止插件
await plugin.stop();
// 清理插件
await plugin.cleanup();

21.4.2 带工具的插件#

插件实现#

bash
typescript

// src/plugin.ts
import {
  Plugin,
  PluginConfig,
  Tool,
  ToolResult,
  PluginContext
} from '@claude-code/plugin-sdk';
import { GreetingTool } from './tools/greeting';
import { TimeTool } from './tools/time';

export class ToolsPlugin extends Plugin {
  private toolManager: any;

  constructor() {
    super({
      name: 'tools-plugin',
      version: '1.0.0',
      description: 'A plugin with tools'
    });

    this.toolManager = {
      register: (tool: Tool) => {},
      execute: async (name: string, params: any, context: PluginContext) => {
        return { success: true, data: {} };
      }
    };
  }

  async initialize(config: PluginConfig): Promise<void> {
    console.log('Tools Plugin initialized');

    // 注册工具
    this.toolManager.register(new GreetingTool());
    this.toolManager.register(new TimeTool());
  }

  async start(): Promise<void> {
    console.log('Tools Plugin started');
  }

  async stop(): Promise<void> {
    console.log('Tools Plugin stopped');
  }

  async cleanup(): Promise<void> {
    console.log('Tools Plugin cleaned up');
  }
}

### Greeting 工具

// src/tools/greeting.ts
import { Tool, ToolResult, PluginContext } from '@claude-code/plugin-sdk';
export class GreetingTool extends Tool {
constructor() {
super({
name: 'greeting',
description: 'Generate a greeting message',
parameters: [
{
name: 'name',
type: 'string',
description: 'The name to greet',
required: true
},
{
name: 'language',
type: 'string',
description: 'The language of the greeting',
required: false,
default: 'english'
}
]
});
}
async execute(params: Record<string, any>, context: PluginContext): Promise<ToolResult> {
const name = params.name;
const language = params.language || 'english';
let greeting: string;
switch (language.toLowerCase()) {
case 'english':
greeting = `Hello, ${name}!`;
break;
case 'spanish':
greeting = `¡Hola, ${name}!`;
break;
case 'french':
greeting = `Bonjour, ${name}!`;
break;
case 'german':
greeting = `Hallo, ${name}!`;
break;
case 'chinese':
greeting = `你好,${name}!`;
break;
default:
greeting = `Hello, ${name}!`;
}
return {
success: true,
data: {
greeting,
language
}
};
}
}

Time 工具#

bash
typescript

// src/tools/time.ts
import { Tool, ToolResult, PluginContext } from '@claude-code/plugin-sdk';

export class TimeTool extends Tool {
  constructor() {
    super({
      name: 'time',
      description: 'Get current time in various formats',
      parameters: [
        {
          name: 'format',
          type: 'string',
          description: 'The format of the time (iso, unix, readable)',
          required: false,
          default: 'iso'
        },
        {
          name: 'timezone',
          type: 'string',
          description: 'The timezone (e.g., UTC, America/New_York)',
          required: false
        }
      ]
    });
  }

  async execute(params: Record<string, any>, context: PluginContext): Promise<ToolResult> {
    const format = params.format || 'iso';
    const timezone = params.timezone;

    let date: Date;
    if (timezone) {
      // 使用指定时区
      date = new Date();
    } else {
      date = new Date();
    }

    let time: string;

    switch (format.toLowerCase()) {
      case 'iso':
        time = date.toISOString();
        break;
      case 'unix':
        time = Math.floor(date.getTime() / 1000).toString();
        break;
      case 'readable':
        time = date.toLocaleString();
        break;
      default:
        time = date.toISOString();
    }

    return {
      success: true,
      data: {
        time,
        format,
        timezone: timezone || 'local'
      }
    };
  }
}

### 使用示例

// 使用插件
import { ToolsPlugin } from './plugin';
const plugin = new ToolsPlugin();
// 初始化插件
await plugin.initialize({});
// 启动插件
await plugin.start();
// 执行工具
const greetingResult = await plugin.toolManager.execute(
'greeting',
{ name: 'World', language: 'chinese' },
{}
);
console.log(greetingResult.data.greeting); // 你好,World!
const timeResult = await plugin.toolManager.execute(
'time',
{ format: 'readable' },
{}
);
console.log(timeResult.data.time); // 2024-01-15 10:30:00
// 停止插件
await plugin.stop();
// 清理插件
await plugin.cleanup();

21.4.3 带命令的插件#

插件实现#

bash
typescript

// src/plugin.ts
import {
  Plugin,
  PluginConfig,
  Command,
  CommandResult,
  PluginContext
} from '@claude-code/plugin-sdk';
import { GreetCommand } from './commands/greet';
import { CalcCommand } from './commands/calc';

export class CommandsPlugin extends Plugin {
  private commandManager: any;

  constructor() {
    super({
      name: 'commands-plugin',
      version: '1.0.0',
      description: 'A plugin with commands'
    });

    this.commandManager = {
      register: (command: Command) => {},
      execute: async (name: string, args: string[], context: PluginContext) => {
        return { success: true, output: '' };
      }
    };
  }

  async initialize(config: PluginConfig): Promise<void> {
    console.log('Commands Plugin initialized');

    // 注册命令
    this.commandManager.register(new GreetCommand());
    this.commandManager.register(new CalcCommand());
  }

  async start(): Promise<void> {
    console.log('Commands Plugin started');
  }

  async stop(): Promise<void> {
    console.log('Commands Plugin stopped');
  }

  async cleanup(): Promise<void> {
    console.log('Commands Plugin cleaned up');
  }
}

### Greet 命令

// src/commands/greet.ts
import { Command, CommandResult, PluginContext } from '@claude-code/plugin-sdk';
export class GreetCommand extends Command {
constructor() {
super({
name: 'greet',
description: 'Greet someone',
parameters: [
{
name: 'name',
type: 'string',
description: 'The name to greet',
required: true,
short: 'n'
},
{
name: 'formal',
type: 'flag',
description: 'Use formal greeting',
required: false,
short: 'f'
}
]
});
}
async execute(args: string[], context: PluginContext): Promise<CommandResult> {
const parsed = this.parseArgs(args);
const name = parsed.name;
const formal = parsed.formal || false;
let greeting: string;
if (formal) {
greeting = `Good day, ${name}. It is a pleasure to meet you.`;
} else {
greeting = `Hey, ${name}! How's it going?`;
}
return {
success: true,
output: greeting
};
}
}

Calc 命令#

bash
typescript

// src/commands/calc.ts
import { Command, CommandResult, PluginContext } from '@claude-code/plugin-sdk';

export class CalcCommand extends Command {
  constructor() {
    super({
      name: 'calc',
      description: 'Perform mathematical calculations',
      parameters: [
        {
          name: 'expression',
          type: 'string',
          description: 'The mathematical expression to evaluate',
          required: true,
          short: 'e'
        },
        {
          name: 'precision',
          type: 'number',
          description: 'Number of decimal places',
          required: false,
          default: 2,
          short: 'p'
        }
      ]
    });
  }

  async execute(args: string[], context: PluginContext): Promise<CommandResult> {
    const parsed = this.parseArgs(args);

    const expression = parsed.expression;
    const precision = parsed.precision || 2;

    try {
      // 安全地评估表达式
      const result = this.evaluateExpression(expression);

      // 格式化结果
      const formatted = result.toFixed(precision);

      return {
        success: true,
        output: `${expression} = ${formatted}`
      };
    } catch (error) {
      return {
        success: false,
        output: '',
        error: error.message,
        exitCode: 1
      };
    }
  }

  private evaluateExpression(expression: string): number {
    // 只允许数字和基本运算符
    const sanitized = expression.replace(/[^0-9+\-*/().]/g, '');

    // 使用 Function 构造函数安全地评估
    return new Function(`return ${sanitized}`)();
  }
}

### 使用示例

// 使用插件
import { CommandsPlugin } from './plugin';
const plugin = new CommandsPlugin();
// 初始化插件
await plugin.initialize({});
// 启动插件
await plugin.start();
// 执行命令
const greetResult = await plugin.commandManager.execute(
'greet',
['--name', 'World', '--formal'],
{}
);
console.log(greetResult.output); // Good day, World. It is a pleasure to meet you.
const calcResult = await plugin.commandManager.execute(
'calc',
['--expression', '2 + 2 * 3', '--precision', '0'],
{}
);
console.log(calcResult.output); // 2 + 2 * 3 = 8
// 停止插件
await plugin.stop();
// 清理插件
await plugin.cleanup();

21.4.4 带钩子的插件#

插件实现#

bash
typescript

// src/plugin.ts
import {
  Plugin,
  PluginConfig,
  Hook,
  HookResult,
  HookEvent,
  PluginContext
} from '@claude-code/plugin-sdk';
import { LoggingHook } from './hooks/logging';
import { ErrorHandlingHook } from './hooks/error-handling';

export class HooksPlugin extends Plugin {
  private hookManager: any;

  constructor() {
    super({
      name: 'hooks-plugin',
      version: '1.0.0',
      description: 'A plugin with hooks'
    });

    this.hookManager = {
      register: (hook: Hook) => {},
      execute: async (type: string, event: HookEvent, context: PluginContext) => {
        return { success: true };
      }
    };
  }

  async initialize(config: PluginConfig): Promise<void> {
    console.log('Hooks Plugin initialized');

    // 注册钩子
    this.hookManager.register(new LoggingHook());
    this.hookManager.register(new ErrorHandlingHook());
  }

  async start(): Promise<void> {
    console.log('Hooks Plugin started');
  }

  async stop(): Promise<void> {
    console.log('Hooks Plugin stopped');
  }

  async cleanup(): Promise<void> {
    console.log('Hooks Plugin cleaned up');
  }
}

### Logging 钩子

// src/hooks/logging.ts
import {
Hook,
HookResult,
HookEvent,
PluginContext,
HookType
} from '@claude-code/plugin-sdk';
export class LoggingHook extends Hook {
constructor() {
super({
name: 'logging',
type: 'before_command' as HookType,
description: 'Log all commands before execution',
priority: 10
});
}
async execute(event: HookEvent, context: PluginContext): Promise<HookResult> {
const command = event.data.command;
const args = event.data.args;
console.log(`[LoggingHook] Executing command: ${command} ${args.join(' ')}`);
return {
success: true
};
}
}

Error Handling 钩子#

bash
typescript

// src/hooks/error-handling.ts
import {
  Hook,
  HookResult,
  HookEvent,
  PluginContext,
  HookType
} from '@claude-code/plugin-sdk';

export class ErrorHandlingHook extends Hook {
  constructor() {
    super({
      name: 'error-handling',
      type: 'on_error' as HookType,
      description: 'Handle errors and provide helpful messages',
      priority: 100
    });
  }

  async execute(event: HookEvent, context: PluginContext): Promise<HookResult> {
    const error = event.data.error;

    console.error(`[ErrorHandlingHook] Error occurred: ${error.message}`);

    // 提供错误建议
    const suggestions = this.getErrorSuggestions(error);

    if (suggestions.length > 0) {
      console.log('[ErrorHandlingHook] Suggestions:');
      suggestions.forEach((suggestion, index) => {
        console.log(`  ${index + 1}. ${suggestion}`);
      });
    }

    return {
      success: true
    };
  }

  private getErrorSuggestions(error: Error): string[] {
    const suggestions: string[] = [];

    if (error.message.includes('permission')) {
      suggestions.push('Check if you have the necessary permissions');
      suggestions.push('Try running with elevated privileges');
    }

    if (error.message.includes('not found')) {
      suggestions.push('Verify the file or resource exists');
      suggestions.push('Check the spelling of the file name');
    }

    if (error.message.includes('network')) {
      suggestions.push('Check your internet connection');
      suggestions.push('Verify the server is running');
    }

    return suggestions;
  }
}

### 使用示例

// 使用插件
import { HooksPlugin } from './plugin';
const plugin = new HooksPlugin();
// 初始化插件
await plugin.initialize({});
// 启动插件
await plugin.start();
// 触发 before_command 钩子
await plugin.hookManager.execute(
'before_command',
{
type: 'before_command',
data: {
command: 'greet',
args: ['--name', 'World']
},
timestamp: new Date()
},
{}
);
// 触发 on_error 钩子
await plugin.hookManager.execute(
'on_error',
{
type: 'on_error',
data: {
error: new Error('Permission denied')
},
timestamp: new Date()
},
{}
);
// 停止插件
await plugin.stop();
// 清理插件
await plugin.cleanup();

21.4.5 完整的插件示例#

插件实现#

bash
typescript

// src/plugin.ts
import {
  Plugin,
  PluginConfig,
  Tool,
  Command,
  Hook,
  ToolResult,
  CommandResult,
  HookResult,
  HookEvent,
  PluginContext
} from '@claude-code/plugin-sdk';
import { GreetingTool } from './tools/greeting';
import { TimeTool } from './tools/time';
import { GreetCommand } from './commands/greet';
import { CalcCommand } from './commands/calc';
import { LoggingHook } from './hooks/logging';
import { ErrorHandlingHook } from './hooks/error-handling';

export class CompletePlugin extends Plugin {
  private toolManager: any;
  private commandManager: any;
  private hookManager: any;
  private logger: any;

  constructor() {
    super({
      name: 'complete-plugin',
      version: '1.0.0',
      description: 'A complete plugin with tools, commands, and hooks'
    });

    this.toolManager = {
      register: (tool: Tool) => {},
      execute: async (name: string, params: any, context: PluginContext) => {
        return { success: true, data: {} };
      }
    };

    this.commandManager = {
      register: (command: Command) => {},
      execute: async (name: string, args: string[], context: PluginContext) => {
        return { success: true, output: '' };
      }
    };

    this.hookManager = {
      register: (hook: Hook) => {},
      execute: async (type: string, event: HookEvent, context: PluginContext) => {
        return { success: true };
      }
    };

    this.logger = {
      info: (message: string) => console.log(`[INFO] ${message}`),
      error: (message: string) => console.error(`[ERROR] ${message}`)
    };
  }

  async initialize(config: PluginConfig): Promise<void> {
    this.logger.info('Complete Plugin initialized');

    // 注册工具
    this.toolManager.register(new GreetingTool());
    this.toolManager.register(new TimeTool());

    // 注册命令
    this.commandManager.register(new GreetCommand());
    this.commandManager.register(new CalcCommand());

    // 注册钩子
    this.hookManager.register(new LoggingHook());
    this.hookManager.register(new ErrorHandlingHook());
  }

  async start(): Promise<void> {
    this.logger.info('Complete Plugin started');
  }

  async stop(): Promise<void> {
    this.logger.info('Complete Plugin stopped');
  }

  async cleanup(): Promise<void> {
    this.logger.info('Complete Plugin cleaned up');
  }
}

### 使用示例

// 使用插件
import { CompletePlugin } from './plugin';
const plugin = new CompletePlugin();
// 初始化插件
await plugin.initialize({});
// 启动插件
await plugin.start();
// 使用工具
const greetingResult = await plugin.toolManager.execute(
'greeting',
{ name: 'World', language: 'chinese' },
{}
);
console.log(greetingResult.data.greeting); // 你好,World!
// 使用命令
const greetResult = await plugin.commandManager.execute(
'greet',
['--name', 'World', '--formal'],
{}
);
console.log(greetResult.output); // Good day, World. It is a pleasure to meet you.
// 触发钩子
await plugin.hookManager.execute(
'before_command',
{
type: 'before_command',
data: {
command: 'greet',
args: ['--name', 'World']
},
timestamp: new Date()
},
{}
);
// 停止插件
await plugin.stop();
// 清理插件
await plugin.cleanup();

21.4.6 插件测试示例#

测试文件#

bash
typescript

// __tests__/plugin.test.ts
import { CompletePlugin } from '../src/plugin';

describe('CompletePlugin', () => {
  let plugin: CompletePlugin;

  beforeEach(() => {
    plugin = new CompletePlugin();
  });

  afterEach(async () => {
    try {
      await plugin.cleanup();
    } catch (error) {
      // 忽略清理错误
    }
  });

  test('should initialize successfully', async () => {
    await expect(plugin.initialize({})).resolves.not.toThrow();

    const status = plugin.getStatus();
    expect(status.name).toBe('complete-plugin');
    expect(status.version).toBe('1.0.0');
  });

  test('should start successfully', async () => {
    await plugin.initialize({});
    await expect(plugin.start()).resolves.not.toThrow();
  });

  test('should stop successfully', async () => {
    await plugin.initialize({});
    await plugin.start();
    await expect(plugin.stop()).resolves.not.toThrow();
  });

  test('should cleanup successfully', async () => {
    await plugin.initialize({});
    await plugin.start();
    await plugin.stop();
    await expect(plugin.cleanup()).resolves.not.toThrow();
  });
});

### 工具测试

// __tests__/tools/greeting.test.ts
import { GreetingTool } from '../../src/tools/greeting';
describe('GreetingTool', () => {
let tool: GreetingTool;
beforeEach(() => {
tool = new GreetingTool();
});
test('should generate English greeting', async () => {
const result = await tool.execute(
{ name: 'World', language: 'english' },
{}
);
expect(result.success).toBe(true);
expect(result.data.greeting).toBe('Hello, World!');
});
test('should generate Chinese greeting', async () => {
const result = await tool.execute(
{ name: 'World', language: 'chinese' },
{}
);
expect(result.success).toBe(true);
expect(result.data.greeting).toBe('你好,World!');
});
test('should validate parameters', () => {
const result = tool.validate({});
expect(result.valid).toBe(false);
expect(result.errors).toContain('Missing required parameter: name');
});
});

命令测试#

bash
typescript

// __tests__/commands/greet.test.ts
import { GreetCommand } from '../../src/commands/greet';

describe('GreetCommand', () => {
  let command: GreetCommand;

  beforeEach(() => {
    command = new GreetCommand();
  });

  test('should greet informally', async () => {
    const result = await command.execute(
      ['--name', 'World'],
      {}
    );

    expect(result.success).toBe(true);
    expect(result.output).toContain('Hey, World!');
  });

  test('should greet formally', async () => {
    const result = await command.execute(
      ['--name', 'World', '--formal'],
      {}
    );

    expect(result.success).toBe(true);
    expect(result.output).toContain('Good day, World.');
  });

  test('should parse arguments correctly', () => {
    const parsed = command.parseArgs(['--name', 'World', '--formal']);

    expect(parsed.name).toBe('World');
    expect(parsed.formal).toBe(true);
  });
});

### 运行测试

# 运行所有测试
npm test
# 运行特定测试文件
npm test -- plugin.test.ts
# 运行测试并生成覆盖率报告
npm test -- --coverage
# 监听模式
npm run test:watch

标记本节教程为已读

记录您的学习进度,方便后续查看。